package com.flying.acls.domain.service;

import com.flying.acls.model.jdbc.AclEntryRepository;
import com.flying.acls.model.jdbc.ResExprRepository;
import com.flying.acls.domain.context.ExpressionContext;
import com.flying.acls.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

public class AclServiceImpl implements AclService {
    @Autowired
    private ResExprRepository resExprRepository;
    @Autowired
    private AclEntryRepository aclEntryRepository;
    @Autowired
    private Matcher matcher;

    @Override
    public boolean isGranted(ExpressionContext context, List<ResExpr> requiredResExprs, List<SidExpr> sidExprs, List<PermsExpr> requiredPermsExpr) {
        for (ResExpr resExpr : requiredResExprs) {
            if (isGranted(context, resExpr, sidExprs, requiredPermsExpr)) {
                return true;
            }
        }
        return false;
    }

    private boolean isGranted(ExpressionContext context, ResExpr requiredResExpr, List<SidExpr> sidExprs, List<PermsExpr> requiredPermsExpr) {
        List<ResExpr> resExprs = readMatchingResources(context, requiredResExpr);
        for (ResExpr resExpr : resExprs) {
            Iterable<AclEntry> aces = readAces(resExpr);
            for (AclEntry ace : aces) {
                for (PermsExpr p : requiredPermsExpr) {
                    if (!matcher.match(context, ace.getPermsExpr(), p)) continue;
                    for (SidExpr sidExpr : sidExprs) {
                        if (!matcher.match(context, ace.getSidExpr(), sidExpr)) continue;
                        return ace.isGranted();
                    }
                }
            }
        }
        return false;
    }

    private List<ResExpr> readMatchingResources(ExpressionContext context, ResExpr resExpr) {
        QResExpr qResExpr = QResExpr.resExpr;
        Iterable<ResExpr> resources = resExprRepository.findAll(qResExpr.type.eq(resExpr.getType()));
        List<ResExpr> passResExprs = new ArrayList<>();
        resources.forEach((res) -> {
            if (matcher.match(context, res, resExpr)) passResExprs.add(res);
        });
        return passResExprs;
    }

    private Iterable<AclEntry> readAces(ResExpr resExpr) {
        QAclEntry qAclEntry = QAclEntry.aclEntry;
        return aclEntryRepository.findAll(qAclEntry.resExpr.id.eq(resExpr.getId()));
    }
}
